
export  interface  INode<T> {
    key:number,
    children:INode<T>[],
    data:T,
    __parent__?:INode<T>;
}

export  interface BinaryNode<T> {
    insert:(node:INode<T>,newNode:INode<T>) => void,
}

export interface BaseNodes<T> extends Array<INode<T>>{}

export class CommonNode<T> implements BinaryNode<T>{
    private root:BaseNodes<T> | INode<T>;

    insert(node:INode<T>,newNode:INode<T>){
        if(newNode.key <= node.key) throw  new Error("newNode key min or == node key");

        if(!this.root){
            this.root= node;
            newNode.__parent__ = this.root;
            this.root.children.push(newNode);
        }else{
            let f_node = this.findNode(node);
            if(f_node){
                newNode.__parent__ = f_node;
                f_node.children.push(newNode);
            }else{
                throw new Error("node not find!!");
            }
        }
    }

    findNode(node:INode<T>):INode<T>| null{
        if(this.root instanceof  Array){
            return this._findNode(this.root,node);
        }else{
            if(this.root.key === node.key){
                return this.root
            }else {
                if (this.root.children) {
                    return  this._findNode(this.root.children, node);
                }
                return null
            }
        }
    }

    remove(node:INode<T> | null){
        if(node){
            let f_node = this.findNode(node);
            if(f_node && f_node.__parent__){
                let index= f_node.__parent__.children.findIndex((it:any) => it.key === node.key)
                if(index !=-1 ){
                    f_node.__parent__.children.splice(index, 1);
                }
            }
        }

    }

    setRoot(nodes:BaseNodes<T> | INode<T>){
        this.root = nodes;
    }

    private _findNode(children: INode<T>[], node: INode<T>):any {
        let find = children.find(it => it.key === node.key);
        if(!!find) return find;
        else{
            for (let i = 0; i < children.length; i++) {
                return this._findNode(children[i].children, node);
            }
        }
    }
}
